package com.authine.cloudpivot.ext.applicationservice.report;

import com.alibaba.cola.dto.RequestContext;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.alibaba.cola.exception.Exceptions;
import com.alibaba.fastjson.JSONObject;
import com.authine.cloudpivot.ext.applicationservice.organization.UserApi;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.dto.system.UserDto;
import com.authine.cloudpivot.ext.enums.report.ReportType;
import com.authine.cloudpivot.ext.model.Report;
import com.authine.cloudpivot.ext.model.ReportRecord;
import com.authine.cloudpivot.ext.utils.BoServiceUtils;
import com.authine.cloudpivot.ext.utils.system.SessionUser;
import com.authine.cloudpivot.file.res.AttachmentRes;
import com.authine.common.util.DateUtils;
import com.authine.common.util.ObjectMapperUtils;
import com.authine.hermes.app.file.feign.FtpFeignServiceI;
import com.authine.hermes.app.launcher.service.BOService;
import com.authine.mvp.app.launcher.domain.SelectorFormat;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.authine.mvp.app.launcher.dto.bo.query.ListBOQuery;
import com.authine.mvp.app.launcher.dto.bo.query.expression.Operator;
import com.authine.mvp.app.launcher.dto.bo.query.expression.SimpleCondition;
import com.authine.mvp.app.launcher.dto.bo.response.BO;
import com.authine.mvp.app.launcher.dto.bo.response.BOList;
import com.authine.mvp.app.launcher.dto.errorcode.ErrorCodeEnum;
import com.google.common.collect.Maps;
import com.lowagie.text.pdf.PdfWriter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.extern.slf4j.Slf4j;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.HtmlExporter;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter;
import net.sf.jasperreports.engine.export.ooxml.JRPptxExporter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import net.sf.jasperreports.export.Exporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimplePdfExporterConfiguration;
import org.apache.commons.io.FileUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.authine.hermes.app.launcher.service.ApplicationServiceFoundation.CUSTOM_SERVICE;

/**
 * @ClassName ReportService
 * @Description: 报表导出
 * @Author fj
 * @Date 2021/8/3
 * @Version V1.0
 **/
@Path("ReportService")
@Slf4j
public class ReportService {
    private static final DefaultJasperReportsContext defaultJasperReportsContext = DefaultJasperReportsContext.getInstance();
    private static final FtpFeignServiceI ftpFeignServiceI = ApplicationContextUtils.getBean(FtpFeignServiceI.class);
    private static final BOService boService = ApplicationContextUtils.getBean(BOService.class);
    private static final UserApi userApi = new UserApi();

    /**
     * 报表导出
     *
     * @param reportRecord 导出信息
     * @return 下载信息
     */
    @Path("exportReport")
    @POST
    @Operation(summary = "报表导出", tags = CUSTOM_SERVICE)
    public SingleResponse<BO> exportReport(@Parameter ReportRecord reportRecord) {
        log.info(this.getClass().getClassLoader().toString());
        SessionUser sessionUser = new SessionUser();
        if (StringUtils.isEmpty(reportRecord.getCode())) {
            throw new BizException("无效的报表代码");
        }
        if (ReportType.get(reportRecord.getType()) == null) {
            throw new BizException("不支持的报表导出类型");
        }
        // 根据code查询报表
        SingleResponse<BOList> reportBOList = BoServiceUtils.findListBySimple(ModelConst.T_REPORT, "code", reportRecord.getCode());
        if (!reportBOList.isSuccess()) {
            Exceptions.throwBizException(ErrorCodeEnum.BO_LIST_ERROR);
        }
        if (reportBOList.getData().getTotal() == 0) {
            throw new BizException(reportRecord.getCode() + "报表代码没找到");
        }
        Report report = ObjectMapperUtils.fromJSON(JSONObject.toJSONString(reportBOList.getData().getData().get(0)), Report.class);
        log.info("------------------------根据报表名称获取报表信息：{}------------------------", JSONObject.toJSONString(report));
        // 添加报表导出记录
        Map<String, Object> reportRecordMap = Maps.newHashMap();
        // 获取当前用户信息
        UserDto user = userApi.getUser(UserDto.builder().id(RequestContext.getCorpLoginId()).build());
        SelectorFormat userFormat = sessionUser.getUserFormat(user.getId(), user.getFullname());
//        String code;
//        if (reportRecord.getCode().contains("/")) {
//            code = reportRecord.getCode().substring(reportRecord.getCode().lastIndexOf("/") + 1, reportRecord.getCode().lastIndexOf("."));
//        } else {
//            code = reportRecord.getCode().substring(0, reportRecord.getCode().lastIndexOf("."));
//        }
        reportRecordMap.put("code", report.getCode());
        reportRecordMap.put("params_url", "");
        reportRecordMap.put("organization_id", user.getOrganization_id().get(0));
        reportRecordMap.put("organization_name", user.getOrganization_name());
        reportRecordMap.put("user_id", userFormat);
        reportRecordMap.put("user_name", user.getUsername());
        reportRecordMap.put("type", reportRecord.getType());
        reportRecordMap.put("description", reportBOList.getData().getData().get(0).get("description"));

        // 新增报表导出记录
        SingleResponse<BO> recordCreateBO = BoServiceUtils.createBo(ModelConst.T_REPORT_RECORD, Collections.singletonList(reportRecordMap));
        log.info("------------------------报表导出数据新增成功：{}------------------------", JSONObject.toJSONString(reportRecordMap));
        if (!recordCreateBO.isSuccess()) {
            Exceptions.throwBizException(ErrorCodeEnum.BO_SAVE_ERROR);
        }

        Map<String, Object> updateMap = Maps.newHashMap();
        updateMap.put("id", recordCreateBO.getData().getBizObjectId());
        // 报表下载
        try {
            log.info("------------------------报表下载-----");
            AttachmentRes attachmentRes = export(report, reportRecord.getType(), reportRecord.getData());
            updateMap.put("download_path", attachmentRes.getUrl());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("----------------报表下载出现异常，异常信息：{}----------------", e.getMessage());
            StringBuilder errorInfo = new StringBuilder();
            errorInfo.append(recordCreateBO.getData().getData().get("error_info") == null ? "" : recordCreateBO.getData().getData().get("error_info").toString())
                    .append(DateUtils.format2DateStr2(new Date()))
                    .append("-")
                    .append(e.getMessage());
            updateMap.put("error_info", errorInfo);
        }

        SingleResponse<BO> recordUpdateBO = BoServiceUtils.updateMainBo(ModelConst.T_REPORT_RECORD, updateMap);
        log.info("------------------------导出记录修改成功，修改后结果：{}------------------------", JSONObject.toJSONString(recordUpdateBO));
        return recordUpdateBO;
    }

    /**
     * 获取公告信息
     *
     * @return 公告信息
     */
    @Path("getNotice")
    @POST
    @Operation(summary = "获取公告信息", tags = CUSTOM_SERVICE)
    public SingleResponse<String> getNotice() {
        String notice = "尊敬的平台用户，近期原材料迎来涨价潮，特此提醒：针对年度寻源的业务，请务必重视商品当期价格的变化，有任何问题请及时与平台沟通，感谢您的配合。        ";
        return SingleResponse.of(notice);
    }

    /**
     * 获取报表导出列表
     *
     * @return 报表导出列表
     */
    @Path("reportRecordList")
    @POST
    @Operation(summary = "获取报表导出列表", tags = CUSTOM_SERVICE)
    public SingleResponse<BOList> reportRecordList(@Parameter ListBOQuery listBOQuery) {
        SessionUser sessionUser = new SessionUser();
        SelectorFormat userFormat = sessionUser.getUserFormat();
        log.info("-----------------------userFormat：{}------------------------", JSONObject.toJSONString(userFormat));
        // 设置查询条件
        listBOQuery.setSchemaCode(ModelConst.T_REPORT_RECORD);
        SimpleCondition condition = new SimpleCondition("user_id", Operator.EQ, sessionUser.getCurrentUserId());
        listBOQuery.setCondition(condition);
        // 获取报表导出列表
        SingleResponse<BOList> reportRecodeList = boService.list(listBOQuery);
        log.info("---------根据用户id获取报表导出列表返回结果：{}-------------", JSONObject.toJSONString(reportRecodeList));
        return reportRecodeList;
    }

    /**
     * 报表导出
     *
     * @param report        报表模板
     * @param type          报表类型
     * @param data          报表数据
     * @return 附件下载信息
     * @throws JRException JRException
     * @throws IOException IOException
     */
    @SuppressWarnings("unchecked")
    private AttachmentRes export(Report report, String type, List<Map<String, Object>> data) throws JRException, IOException {
        if (!report.getEnabled()) {
            throw new BizException(report.getCode() + "报表被暂停使用");
        }
        // 获取jasperReports模板文件
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(report.getTemplate());
        // 设置jasperReports配置信息
        JasperCompileManager jasperCompileManager = JasperCompileManager.getInstance(defaultJasperReportsContext);
        JasperDesign jasperDesign = JRXmlLoader.load(defaultJasperReportsContext, inputStream);
        JasperReport jasperReport = jasperCompileManager.compile(jasperDesign);
        // 获取报表格式
        ReportType reportType = ReportType.get(type);
        // 设置报表数据
        JRDataSource dataSource = new JRBeanCollectionDataSource(data);
        Map<String, Object> parameterValue = Maps.newHashMap();
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameterValue, dataSource);
        // 临时文件名称
        String tempFileName = "/tmp/" + System.currentTimeMillis() + "." + reportType.getId();
        Exporter exporter = null;

        if (ReportType.Pdf.getId().equals(reportType.getId())) {
            exporter = new JRPdfExporter();
            SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
            configuration.setPermissions(PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_PRINTING);
            exporter.setConfiguration(configuration);
        } else if (ReportType.Xls.getId().equals(reportType.getId())) {
            exporter = new JRXlsExporter();
        } else if (ReportType.Xlsx.getId().equals(reportType.getId())) {
            exporter = new JRXlsxExporter();
        } else if (ReportType.Docx.getId().equals(reportType.getId())) {
            exporter = new JRDocxExporter();
        } else if (ReportType.Pptx.getId().equals(reportType.getId())) {
            exporter = new JRPptxExporter();
        } else if (ReportType.Html.getId().equals(reportType.getId())) {
            exporter = new HtmlExporter();
        }
        if (exporter == null) {
            throw new BizException("请选择合适的报表导出格式");
        }
        // 设置报表输入流
        exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
        // 设置报表输出流
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(tempFileName));
        // 报表导出
        exporter.exportReport();
        log.info("--------------File exported successfully，tempFileName：{}----------------------", tempFileName);

        // 上传文件到云枢系统中
        File file = new File(tempFileName);
        byte[] bytes = FileUtils.readFileToByteArray(file);
        String downLoadFileName = report.getCode() + "." + reportType.getId();
        MultipartFile mockMultipartFile = new MockMultipartFile("file", downLoadFileName, reportType.getName(), bytes);
        SingleResponse<AttachmentRes> upload = ftpFeignServiceI.uploadAttachment(mockMultipartFile);
        log.info("---------File upload successfully：{}-------------", JSONObject.toJSONString(upload.getData()));

        // 删除临时文件
        boolean delete = file.delete();
        log.info("---------Delete tempFile result：{}-------------", delete);
        return upload.getData();
    }
}
